package ee.metadataUtils.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Binder;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.name.Names;
import com.google.inject.util.Types;

import ee.metadataUtils.ObjectFactory;
import ee.metadataUtils.PropertyMetadataAdapterFactory;
import ee.metadataUtils.PropertyMetadataAdapterFactoryRegistry;
import ee.metadataUtils.PropertyMetadataFactory;
import ee.metadataUtils.PropertyMetadataRegistry;
import ee.metadataUtils.PropertyMetadataType;

public class MetadataUtilsModule extends AbstractModule {

	static public <T> void bindAdapter(Binder binder, Class<T> interfaceType, Class<? extends T> adapterType)
	{
		//create an adapterFactoryType
		@SuppressWarnings("unchecked")
		TypeLiteral<PropertyMetadataAdapterFactory<?>> adapterFactoryType = (TypeLiteral<PropertyMetadataAdapterFactory<?>>) TypeLiteral.get(Types.newParameterizedType(PropertyMetadataAdapterFactory.class, interfaceType));
		
		//create a factory using the given information
		binder.install(new FactoryModuleBuilder().implement(interfaceType, adapterType).build(adapterFactoryType));
		
		//register the factory
		MapBinder<Class<?>, PropertyMetadataAdapterFactory<?>> propertyMetadataFactories = MapBinder.newMapBinder(binder, new TypeLiteral<Class<?>>(){}, new TypeLiteral<PropertyMetadataAdapterFactory<?>>(){});
		propertyMetadataFactories.addBinding(interfaceType).to(adapterFactoryType);
	}
	
	@Override
	protected void configure() {
		bind(PropertyMetadataRegistry.class).to(GuicePropertyMetadataRegistry.class).asEagerSingleton();
		bind(PropertyMetadataFactory.class).to(GuicePropertyMetadataFactory.class).asEagerSingleton();
		bind(PropertyMetadataAdapterFactoryRegistry.class).to(GuicePropertyMetadataAdapterFactoryRegistry.class).asEagerSingleton();
		bind(ObjectFactory.class).to(GuiceObjectFactory.class).asEagerSingleton();
		
		bind(String.class).annotatedWith(Names.named("forbiddenAnnotationName")).toInstance("Forbidden");
		
		bind(PropertyMetadataType.class).toInstance(PropertyMetadataType.BOTH);
	}

}
